#include "LoadAudio.h"

#include <QByteArray>
#include <QHostAddress>

#include "NetHeader.h"

#ifndef FRAME_LEN_125MS
#define FRAME_LEN_125MS 1900
#endif

extern QUEUE_DATA<MESG> g_recvAudioQueue; //音频接收队列

LoadAudio::LoadAudio(QObject *parent)
    : QThread{parent}
{
    QAudioFormat format;
    format.setSampleRate(8000);
    format.setChannelCount(1);
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::UnSignedInt);

    QAudioDeviceInfo info = QAudioDeviceInfo::defaultOutputDevice();
    if (!info.isFormatSupported(format))
    {
        qWarning() << "Raw audio format not supported by backend, cannot play audio.";
        return;
    }
    m_audioOutput = new QAudioOutput(format, this);
    connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(onHandleStateChanged(QAudio::State)));
    m_outputDevice = nullptr;
}

LoadAudio::~LoadAudio()
{
    m_audioOutput->deleteLater();
}

QString LoadAudio::errorString()
{
    if (m_audioOutput->error() == QAudio::OpenError)
    {
        return QString("AudioOutput An error occurred opening the audio device").toUtf8();
    }
    else if (m_audioOutput->error() == QAudio::IOError)
    {
        return QString("AudioOutput An error occurred during read/write of audio device").toUtf8();
    }
    else if (m_audioOutput->error() == QAudio::UnderrunError)
    {
        return QString("AudioOutput Audio data is not being fed to the audio device at a fast enough rate").toUtf8();
    }
    else if (m_audioOutput->error() == QAudio::FatalError)
    {
        return QString("AudioOutput A non-recoverable error has occurred, the audio device is not usable at this time.");
    }
    else
    {
        return QString("AudioOutput No errors have occurred").toUtf8();
    }
}

void LoadAudio::onHandleStateChanged(QAudio::State state)
{
    switch (state)
    {
    case QAudio::ActiveState:
        LOG << "audio start play!!";
        break;
    case QAudio::StoppedState:
        if (m_audioOutput->error() != QAudio::NoError)
        {
            m_audioOutput->stop();
            emit audiooutputerror(errorString());
            LOG << "out audio error" << m_audioOutput->error();
        }
        break;
    }
}

void LoadAudio::startPlay()
{
    if (m_audioOutput->state() == QAudio::ActiveState){
        return;
    }
    LOG << "start playing audio";
    m_outputDevice = m_audioOutput->start();
}

void LoadAudio::stopPlay()
{
    if (m_audioOutput->state() == QAudio::StoppedState){
        return;
    }
    {
        QMutexLocker lock(&m_deviceLock);
        m_outputDevice = nullptr;
    }
    m_audioOutput->stop();
    LOG << "stop playing audio";
}

void LoadAudio::run()
{
    m_isRun = true;
    QByteArray pcmDataBuffer;
    LOG << "start playing audio";
    while (true) {
        {
            QMutexLocker lock(&m_lock);
            if (m_isRun == false)
            {
                stopPlay();
                LOG << "stop playing audio";
                return;
            }
        }

        auto msg = g_recvAudioQueue.pop_msg();
        if (nullptr == msg) {
            continue;
        }

        {
            QMutexLocker lock(&m_deviceLock);
            if (nullptr == m_outputDevice) {
                pcmDataBuffer.clear();
                delete msg;
                continue;
            }
            pcmDataBuffer.append((char*)msg->data.get(), msg->size);
            if (pcmDataBuffer.size() >= FRAME_LEN_125MS){
                qint64 wroten = m_outputDevice->write(pcmDataBuffer.data(), FRAME_LEN_125MS);
                if (wroten < 0) {
                    LOG << m_outputDevice->errorString();
                    delete msg;
                    return;
                }
                emit speaker(QHostAddress(msg->ip).toString());
                pcmDataBuffer = pcmDataBuffer.right(pcmDataBuffer.size() - wroten);
            }
            delete msg;
        }
    }

}

void LoadAudio::stopImmediately()
{
    QMutexLocker lock(&m_lock);
    m_isRun = false;
}


void LoadAudio::onSetVolumn(int val)
{
    m_audioOutput->setVolume(val / 100.0);
}

void LoadAudio::onClearQueue()
{
    LOG << "audio recv clear";
    g_recvAudioQueue.clear();
}
